package com.citywithincity.models;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.w3c.dom.Element;

import com.citywithincity.db.interfaces.IValueConvert;
import com.citywithincity.utils.ClassUtil;
import com.citywithincity.utils.ConfigReader;
import com.citywithincity.utils.StringUtils;
import com.damai.action.quartz.QuartzJobBuilder;
import com.damai.annotation.Model;
import com.damai.aop.impl.AopBuilder;
import com.damai.timer.impl.TimerUtil;
import com.damai.utils.value.ValueConvertFactory;
import com.damai.xml.XMLUtil;
import com.damai.xml.XMLVisitor;

public class Models {

	private static Map<Class<?>, Object> modelMap = new ConcurrentHashMap<Class<?>, Object>();
	private static Map<String, Object> beanMap = new ConcurrentHashMap<String, Object>();

	private static boolean isLoaded;

	@SuppressWarnings("unchecked")
	public static final <T> T getModel(Class<?> clazz) throws ModelNotFoundException {
		T model= (T) modelMap.get(clazz);
		if(model==null){
			throw new ModelNotFoundException();
		}
		return model;
	}
	
	
	@SuppressWarnings("unchecked")
	public static final <T> T getBean(String key){
		return (T) beanMap.get(key);
	}

	@SuppressWarnings("unchecked")
	public static final <T> T getModelAndRegisterIfNeed(Class<?> clazz) {
		T value = (T) modelMap.get(clazz);
		if (value == null) {
			try {
				value = (T) registerModel(clazz);
			} catch (Exception e) {
				throw new RuntimeException("初始化模型" + clazz.getName(), e);
			}
		}
		return value;
	}

	private static Class<?> getModelClass(Model model, Field field) {
		Class<?> clazz = null;
		if (!model.config().equals("")) {
			// 找到config对应的model
			ConfigReader configReader = ConfigReader.getDefault();
			String modelClass = configReader.get(model.config());
			try {
				clazz = Class.forName(modelClass);
			} catch (Exception e) {
				// 这里
				throw new RuntimeException("Config name " + model.config() + " Class not found " + modelClass);
			}

		} else {
			clazz = field.getType();
		}
		return clazz;
	}

	private static void loadBean() {
		try {
			XMLUtil.loadXmlLevel1(Models.class, "bean.xml", new XMLVisitor() {

				@Override
				public void onVisit(Element element) {
					// 解析brean
					try {
						Object model = parseBean(element);
						// 这里进行解析
						parseModel(model);
					} catch (Exception e) {
						e.printStackTrace();
						throw new RuntimeException(e);
					}
				}
			});
		} catch (IOException e) {
			throw new RuntimeException("没有找到bean.xml");
		}
	}

	private static void parseModel(Object model) {
		if (model == null)
			return;
		Class<?> clazz = model.getClass();
		Method[] methods = ClassUtil.getPublicMethods(clazz);
		QuartzJobBuilder.parse(model, methods);
	}

	private static Object parseBean(Element element) throws Exception {
		String className = element.getAttribute("class");
		String id = element.getAttribute("id");
		Class<?> clazz = Class.forName(className);

		List<Element> elements = XMLUtil.getChildNodes(element);
		if (elements.size() > 0) {
			// 查找匹配的
			Constructor<?>[] constructors = clazz.getConstructors();
			if (constructors.length == 0) {
				throw new RuntimeException("Create bean error : class " + clazz + " does not has a constructor");
			}
			Constructor<?> constructor = constructors[0];
			Class<?>[] types = constructor.getParameterTypes();
			
			int index = 0;
			List<Object> values = new ArrayList<Object>(types.length);
			for (Element child : elements) {
				
				if(index >= elements.size() ){
					throw new RuntimeException(String.format("模型[%s]创建失败,参数个数错误",id));
				}
				if(index >= types.length ){
					throw new RuntimeException(String.format("模型[%s]创建失败,参数个数错误",id));
				}
				Class<?> type = types[index];
				if (child.getTagName().equals("constructor-arg")) {
					Object value;
					if (type == String.class) {
						value = child.getAttribute("value");
					} else {
						IValueConvert convert = ValueConvertFactory.create(String.class, type);
						value = convert.convert(child.getAttribute("value"));
					}
					values.add(value);
				} else if (child.getTagName().equals("constructor-model")) {
					throw new RuntimeException("constructor-model is not implemented in this version");
				}
				++index;
			}
			Object model = ClassUtil.newInstance(constructor, values);
			assignModels(model);
			beanMap.put(id, model);
			return model;
		} else {
			// 加载
			Object model = clazz.newInstance();
			assignModels(model);
			beanMap.put(id, model);
			return model;
		}

	}

	@SuppressWarnings("unchecked")
	public static <T> T assignModels(Object target) throws Exception {
		Field[] fields = ClassUtil.getFields(target.getClass());
		for (Field field : fields) {
			Model model = field.getAnnotation(Model.class);
			Object m;
			if (model != null) {
				if (!StringUtils.isEmpty(model.bean())) {
					// bean
					if (!isLoaded) {
						// load
						loadBean();
						isLoaded = true;
					}
					String bean = model.bean();
					m = beanMap.get(bean);
				} else {
					Class<?> clazz = getModelClass(model, field);
					m = modelMap.get(clazz);
					if (m == null) {
						m = Models.registerModel(clazz);
					}
				}
				field.setAccessible(true);
				field.set(target, m);
			}
		}
		return (T) target;
	}

	public static final Object registerModel(Class<?> clazz) throws Exception {
		// 接口,不应该在这里初始化
		if (clazz.isInterface()) {
			throw new RuntimeException("Interface " + clazz.getName() + " should not register in Models");
		}
		// 查找参数列表
		Constructor<?>[] constructors;
		try {
			constructors = clazz.getConstructors();
		} catch (Throwable e) {
			throw new RuntimeException(clazz + "出现错误", e);
		}

		if (constructors.length == 0) {
			throw new RuntimeException("Model " + clazz.getName() + " has no constructor");
		}
		if (modelMap.containsKey(clazz)) {
			return modelMap.get(clazz);
		}
		if (constructors.length == 1) {
			// 查看参数列表，并初始化
			Constructor<?> constructor = constructors[0];
			Class<?>[] types = constructor.getParameterTypes();
			// 查看是否有
			List<Object> models = new ArrayList<Object>(5);
			for (Class<?> type : types) {
				Object model = modelMap.get(type);
				if (model == null) {
					model = registerModel(type);
				}
				models.add(model);
			}
			Object model = ClassUtil.newInstance(constructor, models);
			// 这里需要注意的是，如何防止a引用b，b引用a(或者更复杂的循环引用)的情况
			assignModels(model);
			modelMap.put(clazz, model);
			parseModel(model);
			Object aopProxyModel = AopBuilder.proxy(model);
			TimerUtil.proxy(model, aopProxyModel);
			return aopProxyModel;
		} else {
			throw new RuntimeException("Model " + clazz.getName() + " has more than one constructors");
		}
	}

}
